package com.sunchangming.uniqname;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Paths;

import org.apache.log4j.Logger;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import com.google.protobuf.ServiceException;
import com.sunchangming.uniqname.UniqMsg.AllocateRequest;
import com.sunchangming.uniqname.UniqMsg.BoolResponse;
import com.sunchangming.uniqname.UniqMsg.ConfirmRequest;
import com.sunchangming.uniqname.UniqMsg.UniqNameService.BlockingInterface;

public class UniqClient {
	private final static Logger logger = Logger.getLogger(UniqClient.class);
	private final NioSocketConnector connector;
	private IoSession session = null;

	private final BlockingInterface service;

	UniqClient(String hostname, int port) throws IOException {
		ServiceConfig.init(Paths.get("config.xml"));
		RpcChannel rpcChannel = new RpcChannel();
		service = com.sunchangming.uniqname.UniqMsg.UniqNameService.newBlockingStub(rpcChannel);

		connector = new NioSocketConnector();
		session = connect(connector, hostname, port, rpcChannel);
		if (session == null)
			throw new RuntimeException("connect fail");
		rpcChannel.setSession(session);
	}

	private static IoSession connect(NioSocketConnector connector, String hostname, int port, RpcChannel rpcChannel) {
		final int CONNECT_TIMEOUT = ServiceConfig.getInstance().getConnectTimeout();
		connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
		connector.getFilterChain().addLast(
				"codec",
				new ProtocolCodecFilter(new ProtobufCodecFactory(UniqMsg.UniqNameService
						.newReflectiveBlockingService(null), false)));
		connector.getFilterChain().addLast("logger", new LoggingFilter());
		connector.setHandler(new ClientIoHandler(rpcChannel));
		for (int i = 0; i != ServiceConfig.getInstance().getConnectRetryTimes(); ++i) {
			try {
				ConnectFuture future = connector.connect(new InetSocketAddress(hostname, port));
				future.awaitUninterruptibly();
				return future.getSession();
			} catch (RuntimeIoException e) {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					break;
				}
			}
		}
		return null;
	}

	public BlockingInterface getService() {
		return service;
	}

	public boolean allocate(String name, String group) {
		try {
			AllocateRequest req = AllocateRequest.newBuilder().setGroup(name).setName(group).build();
			BoolResponse myResponse = this.getService().allocate(new ClientRpcController(), req);
			return myResponse.getValue();
		} catch (ServiceException ex) {
			logger.error("err", ex);
			return false;
		}
	}

	public boolean confirm(String name, String group) {
		try {
			ConfirmRequest req = ConfirmRequest.newBuilder().setGroup(name).setName(group).build();
			BoolResponse myResponse = this.getService().confirm(new ClientRpcController(), req);
			return myResponse.getValue();
		} catch (ServiceException ex) {
			logger.error("err", ex);
			return false;
		}
	}

	public boolean clearAll() {
		try {
			com.sunchangming.uniqname.UniqMsg.Empty req = com.sunchangming.uniqname.UniqMsg.Empty.newBuilder().build();
			BoolResponse myResponse = this.getService().clearAll(new ClientRpcController(), req);
			return myResponse.getValue();
		} catch (ServiceException ex) {
			logger.error("err", ex);
			return false;
		}
	}

	public boolean register(int localid) {
		try {
			com.sunchangming.uniqname.UniqMsg.RegisterMySelfRequest req = com.sunchangming.uniqname.UniqMsg.RegisterMySelfRequest
					.newBuilder().setLocalid(localid).build();
			BoolResponse myResponse = this.getService().registerMySelf(new ClientRpcController(), req);
			return myResponse.getValue();
		} catch (ServiceException ex) {
			logger.error("err", ex);
			return false;
		}
	}

	void shutdown() {
		if (session != null) {
			session.close(false);
			session = null;
		}
		if (connector != null) {
			connector.dispose();
		}
	}
}
